import { u8, u16, u32, u64, i64, toU64, toU32 } from '../types';
import { MemoryManager } from '../memory/MemoryManager';

// 指令前缀类型
export enum PrefixType {
  LOCK = 0xF0,
  REPNE = 0xF2,
  REP = 0xF3,
  CS = 0x2E,
  DS = 0x3E,
  ES = 0x26,
  FS = 0x64,
  GS = 0x65,
  SS = 0x36,
  OPERAND_SIZE = 0x66,
  ADDRESS_SIZE = 0x67
}

// 指令操作码类型
interface Opcode {
  opcode: u8;
  modrm: boolean;
  imm: number;  // 立即数大小(0=无, 1=8位, 2=16位, 4=32位, 8=64位)
  displacement: number;  // 位移量大小
}

// 解码后的指令
export interface DecodedInstruction {
  prefixes: Set<PrefixType>;
  opcode: u8;
  modrm?: {
    mod: u8;
    reg: u8;
    rm: u8;
    sib?: {
      scale: u8;
      index: u8;
      base: u8;
    };
    displacement?: u64;
  };
  immediate?: u64;
  displacement?: u64;
  size: number;  // 指令总长度
}

export class InstructionDecoder {
  private memoryManager: MemoryManager;

  constructor(memoryManager: MemoryManager) {
    this.memoryManager = memoryManager;
  }

  // 解码指令
  decodeInstruction(address: u64): DecodedInstruction {
    const instruction: DecodedInstruction = {
      prefixes: new Set(),
      opcode: 0,
      size: 0
    };

    let currentAddr = address;
    
    // 1. 解码前缀
    currentAddr = this.decodePrefixes(currentAddr, instruction);
    
    // 2. 解码操作码
    const opcodeByte = this.readByte(currentAddr);
    instruction.opcode = opcodeByte;
    currentAddr = toU64(Number(currentAddr) + 1);
    
    // 3. 根据操作码决定是否需要解码ModR/M和SIB字节
    const needsModrm = this.needsModrm(opcodeByte);
    if (needsModrm) {
      currentAddr = this.decodeModrm(currentAddr, instruction);
    }
    
    // 4. 解码立即数
    currentAddr = this.decodeImmediate(currentAddr, instruction);
    
    // 5. 计算指令总长度
    instruction.size = Number(currentAddr) - Number(address);
    
    return instruction;
  }

  // 解码前缀
  private decodePrefixes(address: u64, instruction: DecodedInstruction): u64 {
    let currentAddr = address;
    let prefixCount = 0;
    
    // 最多可以有4个前缀
    while (prefixCount < 4) {
      const byte = this.readByte(currentAddr);
      const prefix = this.getPrefixType(byte);
      
      if (prefix !== undefined) {
        instruction.prefixes.add(prefix);
        currentAddr = toU64(Number(currentAddr) + 1);
        prefixCount++;
      } else {
        break;
      }
    }
    
    return currentAddr;
  }

  // 获取前缀类型
  private getPrefixType(byte: u8): PrefixType | undefined {
    const prefixValues = Object.values(PrefixType);
    if (prefixValues.includes(byte)) {
      return byte as PrefixType;
    }
    return undefined;
  }

  // 判断操作码是否需要ModR/M字节
  private needsModrm(opcode: u8): boolean {
    // 这里是一个简化的判断逻辑
    // 实际的x86指令集有更复杂的规则
    // 大多数两字节操作码和一些单字节操作码需要ModR/M字节
    const modrmRequiredOpcodes = new Set([
      0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x11,
      0x12, 0x13, 0x18, 0x19, 0x1A, 0x1B, 0x20, 0x21, 0x22, 0x23,
      0x28, 0x29, 0x2A, 0x2B, 0x30, 0x31, 0x32, 0x33, 0x38, 0x39,
      0x3A, 0x3B, 0x80, 0x81, 0x82, 0x83, 0x88, 0x89, 0x8A, 0x8B,
      0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x98, 0x99, 0x9A, 0x9B, 0x9C,
      0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA8, 0xA9, 0xAA,
      0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4,
      0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
      0xBF, 0xC0, 0xC1, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA,
      0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4,
      0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
      0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8,
      0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF2, 0xF3,
      0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
    ]);
    
    return modrmRequiredOpcodes.has(opcode);
  }

  // 解码ModR/M字节
  private decodeModrm(address: u64, instruction: DecodedInstruction): u64 {
    let currentAddr = address;
    const modrmByte = this.readByte(currentAddr);
    currentAddr = toU64(Number(currentAddr) + 1);
    
    const mod = (modrmByte >> 6) & 0x03;
    const reg = (modrmByte >> 3) & 0x07;
    const rm = modrmByte & 0x07;
    
    instruction.modrm = {
      mod,
      reg,
      rm
    };
    
    // 检查是否需要SIB字节
    if (mod !== 3 && rm === 4) {
      currentAddr = this.decodeSib(currentAddr, instruction);
    }
    
    // 解码位移量
    if (mod !== 3) {
      if (mod === 0 && rm === 5) {
        // 32位/64位位移量
        const displacement = this.readDword(currentAddr);
        instruction.modrm!.displacement = toU64(displacement);
        currentAddr = toU64(Number(currentAddr) + 4);
      } else if (mod === 1) {
        // 8位位移量
        const displacement = this.readByte(currentAddr);
        instruction.modrm!.displacement = toU64(displacement);
        currentAddr = toU64(Number(currentAddr) + 1);
      } else if (mod === 2) {
        // 32位/64位位移量
        const displacement = this.readDword(currentAddr);
        instruction.modrm!.displacement = toU64(displacement);
        currentAddr = toU64(Number(currentAddr) + 4);
      }
    }
    
    return currentAddr;
  }

  // 解码SIB字节
  private decodeSib(address: u64, instruction: DecodedInstruction): u64 {
    const sibByte = this.readByte(address);
    const scale = (sibByte >> 6) & 0x03;
    const index = (sibByte >> 3) & 0x07;
    const base = sibByte & 0x07;
    
    if (instruction.modrm) {
      instruction.modrm.sib = {
        scale,
        index,
        base
      };
    }
    
    return toU64(Number(address) + 1);
  }

  // 解码立即数
  private decodeImmediate(address: u64, instruction: DecodedInstruction): u64 {
    let currentAddr = address;
    
    // 这里是一个简化的立即数解码逻辑
    // 实际的x86指令集有更复杂的规则
    const immediateSizes = {
      0xB8: 8,  // MOV r64, imm64
      0xB9: 8,
      0xBA: 8,
      0xBB: 8,
      0xBC: 8,
      0xBD: 8,
      0xBE: 8,
      0xBF: 8,
      0x6A: 1,  // PUSH imm8
      0x68: 4,  // PUSH imm32
      0xE8: 4,  // CALL rel32
      0xE9: 4,  // JMP rel32
      0xC7: 4,  // MOV r/m32, imm32
      // 可以根据需要添加更多操作码的立即数大小
    };
    
    const size = immediateSizes[instruction.opcode as keyof typeof immediateSizes];
    if (size) {
      if (size === 1) {
        instruction.immediate = toU64(this.readByte(currentAddr));
      } else if (size === 2) {
        instruction.immediate = toU64(this.readWord(currentAddr));
      } else if (size === 4) {
        instruction.immediate = toU64(this.readDword(currentAddr));
      } else if (size === 8) {
        instruction.immediate = this.readQword(currentAddr);
      }
      currentAddr = toU64(Number(currentAddr) + size);
    }
    
    return currentAddr;
  }

  // 从内存读取一个字节
  private readByte(addr: u64): u8 {
    const data = this.memoryManager.read(addr, 1);
    return data[0];
  }

  // 从内存读取一个字（2字节）
  private readWord(addr: u64): u16 {
    const data = this.memoryManager.read(addr, 2);
    return (data[1] << 8) | data[0];
  }

  // 从内存读取一个双字（4字节）
  private readDword(addr: u64): u32 {
    const data = this.memoryManager.read(addr, 4);
    return (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0];
  }

  // 从内存读取一个四字（8字节）
  private readQword(addr: u64): u64 {
    const data = this.memoryManager.read(addr, 8);
    let result = 0n;
    for (let i = 0; i < 8; i++) {
      result |= BigInt(data[i]) << BigInt(i * 8);
    }
    return result;
  }

  // 打印解码后的指令（调试用）
  printInstruction(instruction: DecodedInstruction): void {
    console.log('Decoded Instruction:');
    console.log(`  Opcode: 0x${instruction.opcode.toString(16).padStart(2, '0')}`);
    
    if (instruction.prefixes.size > 0) {
      console.log('  Prefixes:');
      instruction.prefixes.forEach(prefix => {
        console.log(`    ${PrefixType[prefix]}`);
      });
    }
    
    if (instruction.modrm) {
      console.log('  ModRM:');
      console.log(`    mod: 0x${instruction.modrm.mod.toString(16)}`);
      console.log(`    reg: 0x${instruction.modrm.reg.toString(16)}`);
      console.log(`    rm: 0x${instruction.modrm.rm.toString(16)}`);
      
      if (instruction.modrm.sib) {
        console.log('    SIB:');
        console.log(`      scale: 0x${instruction.modrm.sib.scale.toString(16)}`);
        console.log(`      index: 0x${instruction.modrm.sib.index.toString(16)}`);
        console.log(`      base: 0x${instruction.modrm.sib.base.toString(16)}`);
      }
      
      if (instruction.modrm.displacement !== undefined) {
        console.log(`    Displacement: 0x${instruction.modrm.displacement.toString(16)}`);
      }
    }
    
    if (instruction.immediate !== undefined) {
      console.log(`  Immediate: 0x${instruction.immediate.toString(16)}`);
    }
    
    console.log(`  Size: ${instruction.size} bytes`);
  }
}